package org.pwwong.fxscalautils.components.containers

import com.sun.scenario.scenegraph.fx.FXNode

import java.awt.{Dimension,Insets,Rectangle}

import org.pwwong.fxscalautils.components.layouthints._
import org.pwwong.fxscalautils.Predef._
import org.pwwong.scalautils.property._
import org.pwwong.scalautils.signals.Predef._

import scala.collection.mutable

// Orientation
object Orientation extends Enumeration { val Horiz, Vert = Value }

class Box(_or:Orientation.Value) extends Container { 
   
  override protected def layout {    
    var invPXTotal= 0.0
    var relPCTTotal = 0.0
    val anySizeComps = new mutable.ArrayBuffer[Component]
    
    // Sum Child Widths
    (if(isHoriz) _preferredWidth else _preferredHeight) <= children.foldLeft[Double](0.0){
                         (curTotal, child)=> curTotal + 
                           (getMajorSizeHint(child) match {
                             case PX(pxValue) => invPXTotal += pxValue 
                                                 pxValue.toDouble
                             case PCT(pctValue) => relPCTTotal += pctValue
                                                   getMajorPreferred(child)
                             case Fill() => anySizeComps+=child
                                            getMajorPreferred(child)
                             case Preferred() => invPXTotal += getMajorPreferred(child) 
                                                 getMajorPreferred(child)
					      })
                       }
    
    // Max Child Height
    (if(isHoriz) _preferredHeight else _preferredWidth) <= children.foldLeft[Double](0.0){
                         (max, child)=> Math.max( max, 
                                                  (getMinorSizeHint(child) match {
                                                    case PX(pxValue) => pxValue.toDouble
                                                    case _ => getMinorPreferred(child)
                                                  }) )
                       }
    
    if(children.size > 0){ 
      
      // Set Major Size
      // All children are Invariantly Sized
      if(invPXTotal > 0.0 && relPCTTotal == 0.0){
        val anySizePX = Math.max(getSelfMajorSize()-invPXTotal, 0.0)/anySizeComps.size
        
        for(child <- children)
          getMajorSizeHint(child) match {
            case PX(pxValue) => setMajorSize(child,pxValue)
            case Fill() => setMajorSize(child,anySizePX)
            case Preferred() =>  setMajorSize(child,getMajorPreferred(child))
            case _ =>
          }
        
      // All children are Relatively Sized (%)
      }else if(invPXTotal == 0.0 && relPCTTotal > 0.0){
        val anySizePCT = Math.max(100.0-relPCTTotal, 0.0)/(100.0*anySizeComps.size)
        
        // Get Invariant total
        for(child <- children)
          getMajorSizeHint(child) match {
            case PCT(pctValue) => setMajorSize(child,(pctValue/100.0)*getSelfMajorSize())
            case Fill() => setMajorSize(child,anySizePCT*getSelfMajorSize())
            case Preferred() =>  setMajorSize(child,getMajorPreferred(child))
            case _ => 
          }
      
      // All children are Any Size 
      }else if(invPXTotal == 0.0 && relPCTTotal == 0.0){
        val anySizePX = getSelfMajorSize()/anySizeComps.size
        for(child <- children) setMajorSize(child,anySizePX)
      
      // Children are specified Invariantly and Relatively 
      }else { throw new RuntimeException("Box.layout(): Can't handle %% and px sized children") }
      
      // Set Minor Size
      for(child <- children)
        setMinorSize(child,
                     getMinorSizeHint(child) match {
                       case PCT(pctValue) => (pctValue/100.0)*getSelfMinorSize()
                       case PX(pxValue) => 	 pxValue
                       case Fill() => 		 getSelfMinorSize()
                       case Preferred() =>   getMinorPreferred(child)
                     }
        )
      
      // Set Position
      children.foldLeft[Double](0.0){
        (curMajor, child) => 
        // Major Axis Position
        setMajorPos(child,curMajor)
        // Minor Axis Position
        setMinorPos(child,
                    (if(isHoriz) 
                       child.y().origin match {
                         case TopOrigin => 0.0
                         case MiddleOrigin => getSelfMinorSize()/2.0
                         case BottomOrigin => getSelfMinorSize()
                       }
                     else
                       child.x().origin match {
                         case LeftOrigin => 0.0
                         case CenterOrigin => getSelfMinorSize()/2.0
                         case RightOrigin => getSelfMinorSize()
                       }
                    )+
                      (child.y().offset match {
                        case PX(pxv) => pxv.toDouble
                        case PCT(pctv) => (getSelfMinorSize()*(pctv.toDouble/100.0))
                      })
        )
        curMajor+getMajorSize(child) }
    }
  }
  
  final val orientation:Orientation.Value = _or
  final val isHoriz:Boolean = (_or == Orientation.Horiz)
  
  final protected val getSelfMajorSize:()=>Double = if(isHoriz) curWidth.apply _ else curHeight.apply _
  final protected val getSelfMinorSize:()=>Double = if(isHoriz) curHeight.apply _ else curWidth.apply _
  
  final protected val getMajorSizeHint:(Component)=>SizeValue = if(isHoriz) _.w() else _.h()
  final protected val getMinorSizeHint:(Component)=>SizeValue = if(isHoriz) _.h() else _.w()    
  final protected val getMajorPreferred:(Component)=>Double = if(isHoriz) _.preferredWidth() else _.preferredHeight()
  final protected val getMinorPreferred:(Component)=>Double = if(isHoriz) _.preferredHeight() else _.preferredWidth()     
  final protected val getMajorSize:(Component)=>Double = if(isHoriz) _.curWidth() else _.curHeight()
  final protected val getMinorSize:(Component)=>Double = if(isHoriz) _.curHeight() else _.curWidth()     
  
  final protected val setMajorPos:(Component,Double)=>Unit = if(isHoriz) setX(_)(_) else setY(_)(_)
  final protected val setMinorPos:(Component,Double)=>Unit = if(isHoriz) setY(_)(_) else setX(_)(_)
  final protected val setMajorSize:(Component,Double)=>Unit = if(isHoriz) setWidth(_)(_) else setHeight(_)(_)
  final protected val setMinorSize:(Component,Double)=>Unit = if(isHoriz) setHeight(_)(_) else setWidth(_)(_)
      
  protected val _preferredWidth = Prop[Double]( 0.0 )  
  protected val _preferredHeight = Prop[Double]( 0.0 )  
  override lazy val preferredWidth = _preferredWidth  
  override lazy val preferredHeight = _preferredHeight
}

object HBox {
  def apply() = new Box(Orientation.Horiz) with AccessibleChildren 
}

object VBox {
  def apply() = new Box(Orientation.Vert) with AccessibleChildren
}


